package com.hippo.core.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.hippo.core.dao.CaseDao;
import com.hippo.core.dao.DingTalkConfigDao;
import com.hippo.core.dao.PlanDao;
import com.hippo.core.dao.RecordDao;
import com.hippo.core.util.*;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author: hippo
 */
@Service
@Slf4j
public class PlanService {

    @Autowired
    private PlanDao planDao;

    @Autowired
    private CaseDao caseDao;

    @Autowired
    private QiniuUtils qiniuUtils;

    @Autowired
    private DingTalkConfigDao dingTalkConfigDao;

    @Autowired
    private RecordDao recordDao;

    public JSONObject getPlanList(JSONObject jsonObject) {
        CommonUtil.fillPageParam(jsonObject);
        int count = planDao.countPlan(jsonObject);
        List<JSONObject> list = planDao.getPlanList(jsonObject);
        return CommonUtil.successPage(jsonObject, list, count);
    }

    public JSONObject addPlan(JSONObject data) {
        return CommonUtil.response(planDao.addPlan(data));
    }

    public JSONObject deletePlan(String id) {
        return CommonUtil.response(planDao.deletePlan(id));
    }

    public JSONObject updatePlan(JSONObject data) {
        return CommonUtil.response(planDao.updatePlan(data));
    }

    public List<String> getString(String s) {
        List<String> strs = new ArrayList<String>();
        Pattern p = Pattern.compile("\\$\\{([^}]*)\\}");
        Matcher m = p.matcher(s);
        while (m.find()) {
            strs.add(m.group());
        }
        return strs;
    }

    public JSONObject getCaseListByIds(JSONObject obj) {
        JSONArray caseIds = obj.getJSONArray("caseIds");
        ArrayList<JSONObject> caseList = new ArrayList<>();
//        JSONArray steps = new JSONArray();
        for (int i = 0; i < caseIds.size(); i++) {
//            steps.addAll(JSONArray.parseArray(caseDao.getCaseInfoById((String) caseIds.get(i)).getString("steps")));
            caseList.add(caseDao.getCaseInfoById((String) caseIds.get(i)));
        }
//        ArrayList<String> reList = new ArrayList<>();
//        JSONArray list = new JSONArray();
//        JSONArray finalList = list;
//        caseList.forEach(item -> {
//            String stepsStr = item.getString("steps");
//            JSONArray jsonArray = JSONArray.parseArray(stepsStr);
//            jsonArray.forEach(var -> {
//                JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(var));
//                jsonObject.put("caseName", item.getString("name"));
//                finalList.add(jsonObject);
//            });
//            List<String> stepsStrList = getString(stepsStr);
//            for (int i = 0; i < stepsStrList.size(); i++) {
//                reList.add(stepsStrList.get(i).substring(2, stepsStrList.get(i).length() - 1));
//            }
//        });
//        obj.put("variableList", reList);
//        if (reList.size() > 0) {
//            List<JSONObject> variableList = caseDao.getVariableList(obj);
//            list = StringTools.replaceObj("\\$\\{(", "\\$*)\\}", list, variableList);
//        }
        HashMap<Object, Object> map = new HashMap<>();
        map.put("caseList", caseList);
//        map.put("steps", steps);
//        map.put("variableList", reList);
        return CommonUtil.successJson(map);
    }

    public JSONObject getPlanInfoById(String id) {
        JSONObject list = planDao.getPlanInfoById(id);
        return CommonUtil.successJson(list);
    }

    public JSONArray replaceVariable(JSONObject obj, JSONArray steps) {
        // 替换场景变量
        List<String> variableList = StringTools.getStringList(steps.toString(), "\\$\\{([^}]*)\\}");
        obj.put("variableList", variableList);
        if (variableList.size() > 0) {
            List<JSONObject> variableLists = caseDao.getVariableList(obj);
            steps = StringTools.replaceArray("\\$\\{(", "\\$*)\\}", steps, variableLists);
        }
        return steps;
    }

    public JSONArray replaceExpression(JSONObject obj, JSONArray steps) {
        // 替换场景变量
        List<String> expressionList = StringTools.getStringList(steps.toString(), "\\$\\[([^}]*)\\]");
        obj.put("expressionList", expressionList);
        if (expressionList.size() > 0) {
            List<JSONObject> expressionLists = caseDao.getExpressionList(obj);
            steps = StringTools.replaceArray("\\$\\[(", "\\$*)\\]", steps, expressionLists);
        }
        return steps;
    }

    public JSONArray setCreationMode(JSONArray jsonArray, JSONArray step) {
        step.forEach(item -> {
            JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(item));
            if (jsonObject.getString("parent_id").length() > 1) {
                jsonObject.put("creationMode", 1);
            } else {
                jsonObject.put("creationMode", 2);
            }
            jsonArray.add(jsonObject);
        });
        return jsonArray;
    }

    @XxlJob("runPlan")
    public void job() {
        String jobParam = XxlJobHelper.getJobParam();
        log.info("调度平台传过来的参数：" + jobParam);
        try {
            JSONObject planInfo = planDao.getPlanInfoById(jobParam);
            if (planInfo != null) {
                planInfo.put("caseIds", JSONArray.parseArray(planInfo.getString("caseIds")));
                planInfo.put("createUser", "10000");
                planInfo.put("isHandless", 1);
                planInfo.put("isEndClose", 1);
                planInfo.put("isContinueRun", 0);
                planInfo.put("isShot", 1);
                runPlan(planInfo);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("调度异常：" + e.toString());
        }

    }

    public JSONObject runPlan(JSONObject obj) {
        JSONArray steps = new JSONArray();
        JSONArray caseIdJSONArray = obj.getJSONArray("caseIds");
        for (int i = 0; i < caseIdJSONArray.size(); i++) {
            steps.addAll(JSONArray.parseArray(caseDao.getCaseInfoById((String) caseIdJSONArray.get(i)).getString("steps")));
        }
        // 获取所有用例的id
        caseIdJSONArray = StringTools.caseIdRecursion(caseIdJSONArray, steps);
        obj.put("caseIds", caseIdJSONArray);
        log.info("当前计划下所有的用例id，包括用例里面引用的用例id = " + caseIdJSONArray);
        // 步骤递归，扁平化
        steps = StringTools.stepRecursion(steps);
        // 设置每条步骤的创建方式
        steps = setCreationMode(new JSONArray(), steps);
        // 替换表达式
        steps = replaceExpression(obj, steps);
        // 替换场景变量
        steps = replaceVariable(obj, steps);
        // 生成通用步骤形式
        steps = StringTools.getNewSteps(steps);
        InvokeFunction instance = new InvokeFunction();
        BrowserOpt browserOpt = new BrowserOpt();
        try {
            instance.runConfigInfo(instance, browserOpt, "execHeadless", obj.getIntValue("isHandless"));
            HashMap<String, Object> resultMap = new HashMap<>();
            // 替换文件上传的变量
            int successNum = 0;
            int failNum = 0;
            SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            long topTime = System.currentTimeMillis();
            resultMap.put("topTime", ft.format(new Date()));
            for (int i = 0; i < steps.size(); i++) {
                if (!steps.getJSONObject(i).getString("status").equals("1")) {
                    continue;
                }
                JSONArray optElement = steps.getJSONObject(i).getJSONArray("optElement");
                JSONArray optData = steps.getJSONObject(i).getJSONArray("optData");
                optElement.addAll(optData);
                //初始时间
                long startTime = System.currentTimeMillis();
                steps.getJSONObject(i).put("startTime", ft.format(new Date()));
                String res = instance.runInstanceFunction(new BrowserOpt(), steps.getJSONObject(i).getString("func"), optElement, steps.getJSONObject(i).getString("stepName"));
                if (steps.getJSONObject(i).getString("func").equals("execScreenshot")) {
                    File file = UrlToFIle.getFile(res);
                    MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), null, new FileInputStream(file));
                    String imagePath = qiniuUtils.upload(multipartFile);
                    steps.getJSONObject(i).put("imagePath", imagePath);
                    log.info("成功上传屏幕截图到七牛云");
                    res = "success";
                }
                //结束时间
                long endTime = System.currentTimeMillis();
                steps.getJSONObject(i).put("time", endTime - startTime);
                steps.getJSONObject(i).put("endTime", ft.format(new Date()));
                steps.getJSONObject(i).put("result", res);
                if (!res.equals("success") & !steps.getJSONObject(i).getString("func").equals("execScreenshot")) {
                    failNum += 1;
                    if (obj.getIntValue("isShot") == 1) {
                        JSONArray jsonArray1 = new JSONArray();
                        jsonArray1.add(steps.getJSONObject(i).getString("stepName") + "-" + endTime);
                        instance.runInstanceFunction(new BrowserOpt(), "execScreenshot", jsonArray1, "失败截图");
                        File file = UrlToFIle.getFile(QiniuUtils.getFileUrl() + steps.getJSONObject(i).getString("stepName") + "-" + endTime + ".png");
                        MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), null, new FileInputStream(file));
                        String imagePath = qiniuUtils.upload(multipartFile);
                        steps.getJSONObject(i).put("imagePath", imagePath);
                        log.info("成功上传失败截图到七牛云");
                    }
                    if (obj.getIntValue("isContinueRun") == 0) {
                        break;
                    }
                } else {
                    successNum += 1;
                }
            }
            resultMap.put("endTime", ft.format(new Date()));
            resultMap.put("runTime", System.currentTimeMillis() - topTime);
            ArrayList<Object> chartList = new ArrayList<>();
            String[] statusArray = {"成功", "失败", "跳过"};
            int[] valueArray = {successNum, failNum, steps.size() - failNum - successNum};
            for (int i = 0; i < statusArray.length; i++) {
                HashMap<String, Object> map = new HashMap<String, Object>();
                map.put("name", statusArray[i]);
                map.put("value", valueArray[i]);
                chartList.add(map);
            }
            resultMap.put("chartList", chartList);
            resultMap.put("steps", steps);
            resultMap.put("name", obj.getString("name") + "-" + ft.format(new Date()));
            resultMap.put("isHandless", obj.getInteger("isHandless"));
            resultMap.put("isEndClose", obj.getInteger("isEndClose"));
            resultMap.put("isContinueRun", obj.getInteger("isContinueRun"));
            resultMap.put("isShot", obj.getInteger("isShot"));
            resultMap.put("valueArray", valueArray);
            // 用例计划记录表-记录执行结果
            if (obj.getString("id") != null) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("casePlanId", obj.getString("id"));
                jsonObject.put("casePlanName", obj.getString("name") + " - " + ft.format(new Date()));
                jsonObject.put("type", 1);
                jsonObject.put("result", failNum > 0 ? 2 : 1);
                jsonObject.put("createUser", obj.getString("createUser"));
                jsonObject.put("projectId", obj.getString("projectId"));
                jsonObject.put("report", JSON.toJSONString(resultMap));
                recordDao.addRecord(jsonObject);
            }
            instance.runConfigInfo(instance, browserOpt, "execCloseWindow", obj.getIntValue("isEndClose"));
            if (valueArray[1] > 0 & obj.getIntValue("isAlarm") == 1) {
                JSONObject dingTalkObj = dingTalkConfigDao.getDingTalkInfoById(obj.getString("dingTalkId"));
                DingTalkUtils dingTalkUtils = new DingTalkUtils(dingTalkObj.getString("ding_talk_token"), dingTalkObj.getString("ding_talk_secret"));
                String contactPerson = obj.getString("contactPerson") != null ? obj.getString("contactPerson") : "";
                String[] contactPeople = contactPerson.split(",");
                String content = "【%s】执行计划已执行完成，请及时查看。\n开始时间：%s\n结束时间：%s\n执行耗时：%s秒\n成功数：%s\n失败数：%s\n跳过数：%s";
                DingTalkUtils.sendMessageByText(DingTalkUtils.client, String.format(content, obj.getString("name"), resultMap.get("topTime"), resultMap.get("endTime"),
                                Integer.parseInt(resultMap.get("runTime").toString()) / 1000, valueArray[0], valueArray[1], valueArray[2]),
                        Arrays.asList(contactPeople), obj.getInteger("atAll").equals(1) ? true : false);
            }
            return CommonUtil.successJson(resultMap);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.toString());
            return CommonUtil.errorJson(e.getMessage());
        }
    }

}
